﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Webcam.Image
{
    public static class Maths
    {
        public static int Trim(int val, int min, int max)
        {
            if (val < min)
                return min;
            if (val > max)
                return max;
            return val;
        }

        /// <summary>
        /// Find all the px points between 2 points
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public static Point[] PointsAlongLine(Point p1, Point p2)
        {
            List<Point> points = new List<Point>();

            // points are the same, draw a dot
            if (p1.X == p2.X && p1.Y == p2.Y)
            {
                points.Add(new Point(p1.X, p1.Y));
                return points.ToArray();
            }

            if (p1.X == p2.X)
            {
                // draw vertical line as x component doesnt change
                int top;
                int bottom;
                // find the top end of the line
                if (p1.Y > p2.Y)
                {
                    top = p2.Y;
                    bottom = p1.Y;
                }
                else
                {
                    top = p1.Y;
                    bottom = p2.Y;
                }

                for (int y = top; y <= bottom; y++)
                {
                    points.Add(new Point(p1.X, y));
                }
                return points.ToArray();
            }

            if (p1.Y == p2.Y)
            {
                // draw horizontal line as y component doesnt change
                int left;
                int right;
                // find the top end of the line
                if (p1.X > p2.X)
                {
                    left = p2.X;
                    right = p1.X;
                }
                else
                {
                    left = p1.X;
                    right = p2.X;
                }

                for (int x = left; x <= right; x++)
                {
                    points.Add(new Point(x, p1.Y));
                }
                return points.ToArray();
            }


            // so line must be a sloped line
            int run = p2.X - p1.X;
            int rise = p2.Y - p1.Y;

            // check if the line has a 45degree slope
            int if45Slope = 0;
            if (rise == run)
            {
                // vertical 45degree slope
                if45Slope = 1;
            }
            else if (rise == (run * -1))
            {
                if45Slope = -1;
            }

            if (if45Slope != 0)
            {
                // then is a 45 degree slope
                // so 1 px across does 1 px up or down

                Point start2; // the left point
                Point end2;   // the right point

                if (p1.X < p2.X)
                {
                    start2 = p1;
                    end2 = p2;
                }
                else
                {
                    start2 = p2;
                    end2 = p1;
                }

                int y = start2.Y;
                for (int x = start2.X; x <= end2.X; x++, y += if45Slope)
                {
                    points.Add(new Point(x, y));
                }
                return points.ToArray();
            }

            // still a slope, but not a nice slope
            // line equation
            // y = mx + c
            // find the constants
            float m = CalculateLineGradient(p1, p2);
            float c = CalculateLineC(p1, m);

            if (Math.Abs(p1.X - p2.X) > Math.Abs(p1.Y - p2.Y))
            {
                // x difference is greater than y difference

                // for every x from start to finish
                // there must be a y pixel colored

                // get the starting x coordinate
                Point startX;
                Point endX;
                if (p1.X < p2.X)
                {
                    startX = p1;
                    endX = p2;
                }
                else
                {
                    startX = p2;
                    endX = p1;
                }

                // start and end px always colored
                points.Add(new Point(startX.X, startX.Y));

                for (int x = startX.X + 1; x <= endX.X - 1; x++)
                {
                    // now find for this x coordinate, the y px to color
                    int posY = CalculateLineYPoint(x, m, c); ;
                    points.Add(new Point(x, posY));
                }
                // end px always colored
                points.Add(new Point(endX.X, endX.Y));

            }
            else
            {
                // y difference is greater than x difference

                // for every y from start to finish
                // there must be a x pixel colored

                // get the starting y coordinate
                Point startY;
                Point endY;
                if (p1.Y < p2.Y)
                {
                    startY = p1;
                    endY = p2;
                }
                else
                {
                    startY = p2;
                    endY = p1;
                }


                // start px always colored
                points.Add(new Point(startY.X, startY.Y));

                for (int y = startY.Y + 1; y <= endY.Y - 1; y++)
                {
                    // now find for this y coordinate, the x px to color
                    int posX = CalculateLineXPoint(y, m, c);

                    points.Add(new Point(posX, y));
                }

                // end px always colored
                points.Add(new Point(endY.X, endY.Y));

            }
            return points.ToArray();
        }

        /// <summary>
        /// Calculate a lines gradient
        /// </summary>
        /// <param name="p1"></param>
        /// <param name="p2"></param>
        /// <returns></returns>
        public static float CalculateLineGradient(Point p1, Point p2)
        {
            float run = p2.X - p1.X;
            float rise = p2.Y - p1.Y;
            return rise / run;
        }

        /// <summary>
        /// Calculate a ratio of x to y values
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        public static float CalculateLineGradient(Point p)
        {
            float x = p.X;
            float y = p.Y;
            return y / x;
        }

        /// <summary>
        /// Calculate the C in y=mx +c
        /// </summary>
        /// <param name="p">Point on line</param>
        /// <param name="m">Gradient of line</param>
        /// <returns>The c value</returns>
        public static float CalculateLineC(Point p, float m)
        {
            float c = p.Y - m * p.X;
            return c;
        }

        /// <summary>
        /// Calculate the Y px along the line
        /// </summary>
        /// <param name="x">x point</param>
        /// <param name="m">Gradient</param>
        /// <param name="c">Offset from axis at x=0</param>
        /// <returns>Y Point</returns>
        public static int CalculateLineYPoint(int x, float m, float c)
        {
            int posY = (int)Math.Round(x * m + c);
            return posY;
        }

        /// <summary>
        /// Calculate the x point along a line
        /// </summary>
        /// <param name="y">y point</param>
        /// <param name="m">Gradient</param>
        /// <param name="c">Offset from the axis at x=0</param>
        /// <returns>X point</returns>
        public static int CalculateLineXPoint(int y, float m, float c)
        {
            if (float.IsInfinity(m))
            {

            }
            int posX = (int)Math.Round((y - c) / m);
            return posX;
        }


        /// <summary>
        /// Are 2 floats similar
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="difference">Differences allowed</param>
        /// <returns></returns>
        public static bool FloatSimilar(float x, float y, float difference)
        {
            float diff = x - y;
            if (diff < 0)
                diff = -diff;

            if (diff < difference)
                return true;
            return false;


        }
    }
}
